﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Threading.Tasks;
using XchangeStreamer.Core.Subscriber;
using XchangeStreamer.Utilities;

namespace XchangeStreamer.Performance.SDK
{
    public sealed class Connector : IDisposable
    {
        #region Private Fields

        private static Connector connection;
        private Performance client;
        private EndpointAddress address;
        private bool opened = false;
        
        #endregion

        #region Private Const. / Dest.

        private Connector()
        {
        }

        ~Connector()
        {
            this.Disconnect(false);
        }

        #endregion

        #region Public Properties

        public static Connector Connection
        {
            get
            {
                if (connection == null)
                {
                    lock (typeof(Connector))
                    {
                        if (connection == null)
                        {
                            connection = new Connector();
                        }
                    }
                }
                return connection;
            }
        }

        #endregion

        #region Public Methods

        public void Connect(string ip, string port)
        {
            this.Init(ip, port);
            this.client = new Performance(string.IsNullOrEmpty(port) == true ? Settings.NamedPipeBinding : Settings.TcpBinding, this.address);
            this.Start();
        }

        public void Disconnect()
        {
            this.Disconnect(true);
        }

        public void GetPerformanceCounters(out Single cpu, out Single memory, out uint threads, out Single network)
        {
            var perfCounter = this.client.GetPerformanceCounters();
            cpu = perfCounter.CPU;
            memory = perfCounter.Memory;
            threads = perfCounter.Threads;
            network = perfCounter.Network;
        }

        #endregion

        #region Private Methods

        private void Init(string ip, string port)
        {
            this.address = string.IsNullOrEmpty(port) == true ? new EndpointAddress("net.pipe://" + ip + "/Streamer/Performance") : 
                new EndpointAddress("net.tcp://" + ip + ":" + port + "/Streamer/Performance");
        }

        private void Start()
        {
            this.client.InnerChannel.Opening += new EventHandler(InnerChannel_Opening);
            this.client.InnerChannel.Opened += new EventHandler(InnerChannel_Opened);
            this.client.InnerChannel.Faulted += new EventHandler(this.InnerDuplexChannel_Faulted);

            while (!this.opened)
            {
                try
                {
                    this.client.InnerChannel.Open();
                    this.opened = true;
                }
                catch (Exception ex) { Console.WriteLine(ex.Message); }
            }

            Console.WriteLine("Connected.");
        }

        void InnerChannel_Opening(object sender, EventArgs e)
        {
            Console.WriteLine("Opening.");
        }

        void InnerChannel_Opened(object sender, EventArgs e)
        {
            Console.WriteLine("Opened.");
        }

        private void InnerDuplexChannel_Faulted(object sender, EventArgs e)
        {
            Console.WriteLine("Faulted");
            if (this.opened)
            {
                this.Start();

            }
        }

        private void Disconnect(bool suppressFinalize)
        {
            if (this.client != null)
            {
                this.client.Close();
                this.client = null;
            }

            if (suppressFinalize)
            {
                GC.SuppressFinalize(this);
            }
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            this.Disconnect(true);
        }

        #endregion
    }
}